
/**
 * @author Magnus
 *
 */
public class Cpu implements Constants {
	
	/** The queue of processes waiting for CPU time */
	private Queue cpuQueue;
	/** A reference to the statistics collector */
	private Statistics statistics;
	/** The maximum allowed CPU-time per process */
	private long maxCpuTime;

	private Process activeProcess;
	
	public Cpu(Queue cpuQueue, long cpuTime, Statistics statistics, Gui gui) {
		this.cpuQueue = cpuQueue;
		this.statistics = statistics;
		this.maxCpuTime = cpuTime;
		this.activeProcess = null;
	}
	
	/**
	 * Adds a process to the CPU queue.
	 * @param p	The process to be added.
	 */
	public void insertProcess(Process p) {
		cpuQueue.insert(p);
		p.setNofTimesInReadyQueue(p.getNofTimesInReadyQueue()+1);
	}
	
	/**
	 * Places next process from CPU-Queue into
	 * processing unit
	 */
	public Process getNextProcess(){
		if (!cpuQueue.isEmpty()){
			activeProcess = (Process) cpuQueue.removeNext();
			return activeProcess;
		}
		
		return null; //If empty

	}
	
	/**
	 * Calculates the time used by CPU on
	 * the active Process
	 * @return Returns result of processing calculation
	 */
	public int doProcessing(long clock){
		if(activeProcess != null){
			activeProcess.setTimeExitedCpuQueue(clock); // Used in statistics
			if ((activeProcess.getTimeSpentInCpu()+ maxCpuTime) >= activeProcess.getCpuTimeNeeded()){ //If process fulfills required time
				activeProcess.setTimeSpentInCpu(calcRemCpuNeeded());
				statistics.totalCpuTimeSpentProcessing += (calcRemCpuNeeded()); //Update statistics
				return CPUFINISHED;
			}
			else if ((activeProcess.getTimeSpentInCpu() + maxCpuTime) < activeProcess.getCpuTimeNeeded()){ //If process needs more time
				activeProcess.setTimeSpentInCpu((activeProcess.getTimeSpentInCpu() + maxCpuTime));
				return CPUTIMEOUT;
			}
		}
		return -1;//empty
	}
	
	/**
	 * Calculates the remaining CPU-time needed
	 * for completing the active process
	 * @return
	 */
	public long calcRemCpuNeeded(){
		return (activeProcess.getCpuTimeNeeded() - activeProcess.getTimeSpentInCpu()); //(Timed needed - time spent);
	}

	public void endActiveProcess(){
		this.activeProcess = null;
	}
	
	public Process getActiveProcess(long clock){
		if(activeProcess != null){
			activeProcess.setTimeSpentInCpu(activeProcess.getTimeSpentInCpu()+(clock - activeProcess.getTimeExitedCpuQueue()));
			return activeProcess;
		}
		return null;
	}
	
	public int getNofTimesInCpuQueue() {
		return statistics.nofTimesInCpuQueue;
	}

	public void setNofTimesInCpuQueue() {
		statistics.nofTimesInCpuQueue++;
	}
	
	/**
	 * This method is called when a discrete amount of time has passed.
	 * @param timePassed	The amount of time that has passed since the last call to this method.
	 */
	public void timePassed(long timePassed) {
		statistics.cpuQueueLengthTime += cpuQueue.getQueueLength()*timePassed;
		if (cpuQueue.getQueueLength() > statistics.cpuQueueLargestLength) {
			statistics.cpuQueueLargestLength = cpuQueue.getQueueLength(); 
		}
    }

}
